#include <iostream>
#include <fstream>
#include <sstream>
#include <iomanip>
#include <unordered_map>

#include "CSVparser.hpp"

namespace csv {
    
    static std::string _emptyStr = "";

  Parser::Parser(const std::string &data, const DataType &type, char sep)
    : _type(type), _sep(sep)
  {
      std::string line;
      std::string lastStr;
      size_t size;
      unsigned int index = 0;
      if (type == eFILE)
      {
        _file = data;
        std::ifstream ifile(_file.c_str());
        if (ifile.is_open())
        {
            while (ifile.good())
            {
                getline(ifile, line);
                size = line.size();
                if(size > 0){
                  lastStr = line.substr(size-1);
                  if(lastStr == "\r")
                    line = line.substr(0, size-1);
                }
                if(index > 0){
                  if(index < _header.size()+1){
                    parseColumn(index-1, line);
                    index++;
                  }else{
                    if (line != ""){
                      index++;
                      parseContent(line);
                    }
                  }
                }else{
                  if (line != ""){
                    index++;
                    parseHeader(line);
                  }
                }
            }
            ifile.close();

            if (index == 0)
                std::cout << std::string("CSVparser No Data in ").append(_file) << std::endl;
        }
        else
            std::cout << std::string("CSVparser Failed to open ").append(_file) << std::endl;
      }
      else
      {
        std::istringstream stream(data);
        while (std::getline(stream, line)){
          size = line.size();
          if(size > 0){
            lastStr = line.substr(size-1);
            if(lastStr == "\r")
              line = line.substr(0, size-1);
          }
          if(index > 0){
            if(index < _header.size()+1){
              parseColumn(index-1, line);
              index++;
            }else{
              if (line != ""){
                index++;
                parseContent(line);
              }
            }
          }else{
            if (line != ""){
              index++;
              parseHeader(line);
            }
          }
        }

        if (index == 0)
            std::cout << std::string("CSVparser No Data in pure content") << std::endl;
      }
  }

  Parser::~Parser(void)
  {
     _header.clear();

     for (auto it = _content.begin(); it != _content.end(); it++)
          delete it->second;
     _content.clear();

     for (auto it = _columnValues.begin(); it != _columnValues.end(); it++){
          (*it).clear();
     }
     _columnValues.clear();
  }

  void Parser::parseHeader(std::string line)
  {
      std::stringstream ss(line);
      std::string item;

      while (std::getline(ss, item, _sep))
          _header.push_back(item);
      
      _header.shrink_to_fit();

      _columnValues.resize(_header.size());
  }

  void Parser::parseColumn(unsigned int pos, std::string line)
  {
    std::vector<std::string> column = _columnValues[pos];
    if(line != ""){
      bool quoted = false;
      int tokenStart = 0;
      unsigned int i = 0;

      for (; i != line.length(); i++)
      {
           if (line.at(i) == '"')
               quoted = ((quoted) ? (false) : (true));
           else if (line.at(i) == ',' && !quoted)
           {
               column.push_back(line.substr(tokenStart, i - tokenStart));
               tokenStart = i + 1;
           }
      }

      //end
      column.push_back(line.substr(tokenStart, i - tokenStart));
    }else{
      column.push_back("");
    }
    
    column.shrink_to_fit();
    _columnValues[pos] = column;
  }

  void Parser::parseContent(std::string line)
  {
     bool quoted = false;
     int tokenStart = 0;
     unsigned int i = 0;
     unsigned int colIdx = 0;
     std::stringstream ss;
     int tempInt;
      
     Row *row = new Row(this);

     for (; i != line.length(); i++)
     {
          if (line.at(i) == '"')
              quoted = ((quoted) ? (false) : (true));
          else if (line.at(i) == ',' && !quoted)
          {
              ss.clear();
              ss.str("");
              ss << line.substr(tokenStart, i - tokenStart);
              ss >> tempInt;
              row->push(colIdx, tempInt);
              tokenStart = i + 1;
              ++colIdx;
          }
     }

     //end
     ss.clear();
     ss.str("");
     ss << line.substr(tokenStart, i - tokenStart);
     ss >> tempInt;
     row->push(colIdx, tempInt);
      
     // if value(s) missing
      if (row->size() != _header.size()){
         std::cout << _file << std::endl;
         std::cout << line << std::endl;
         std::cout << std::string("CSVparser corrupted data !") << std::endl;
      }else{
          unsigned int val = row->getValueByIndex(0);
          auto ret = _content.emplace(std::make_pair(_columnValues[0][val], row)); // 以第一项值为key保存row
          if(!ret.second){
              std::cout << _file << std::endl;
              std::cout << line << std::endl;
              std::cout << std::string("CSVparser duplicate key , Row leak !") << std::endl;
          }
      }
  }

  const RowDict& Parser::getRows() const
  {
    return _content;
  }

  Row* Parser::getRow(const std::string &key) const
  {
      auto iter = _content.find(key);
      if(iter != _content.end()){
          return iter->second;
      }else{
          return nullptr;
      }
  }

  Row* Parser::operator[](const std::string &key) const
  {
      return getRow(key);
  }

  unsigned int Parser::rowCount(void) const
  {
      return _content.size();
  }

  unsigned int Parser::columnCount(void) const
  {
      return _header.size();
  }

  const std::vector<std::string>& Parser::getHeader(void) const
  {
      return _header;
  }

  const std::string& Parser::getHeaderElement(unsigned int pos) const
  {
      if (pos < _header.size())
          return _header[pos];
      return _emptyStr;
  }

  const std::string& Parser::getColumnElement(unsigned int column, unsigned int pos) const
  {
      if (column < _columnValues.size())
          if (pos < _columnValues[column].size())
              return _columnValues[column][pos];
      return _emptyStr;
  }

  int Parser::getHeaderPos(const std::string &key) const
  {
      std::vector<std::string>::const_iterator it;
      int pos = 0;

      for (it = _header.begin(); it != _header.end(); it++)
      {
          if (key == *it)
              return pos;
          pos++;
      }
      return -1;
  }

//  bool Parser::deleteRow(const std::string &key)
//  {
//    auto iter = _content.find(key);
//    if(iter != _content.end()){
//        delete iter->second;
//        _content.erase(iter);
//        return true;
//    }else{
//        return false;
//    }
//  }

//  bool Parser::addRow(const std::string &key, const std::vector<std::string> &r)
//  {
//    bool ret = false;
//    Row *row = getRow(key);
//    if(row == nullptr){
//      row = new Row(this);
//      ret = true;
//    }else{
//      row->clear();
//    }
//
//      for (unsigned int i = 0 ;i<r.size();++i)
//          row->push(i,r[i]);
//
//    return ret;
//  }

//  void Parser::sync(void) const
//  {
//    if (_type == DataType::eFILE)
//    {
//      std::ofstream f;
//      f.open(_file, std::ios::out | std::ios::trunc);
//
//      // header
//      unsigned int i = 0;
//      for (auto it = _header.begin(); it != _header.end(); it++)
//      {
//        f << *it;
//        if (i < _header.size() - 1)
//          f << ",";
//        else
//          f << std::endl;
//        i++;
//      }
//
//      for (auto it = _content.begin(); it != _content.end(); it++)
//        f << *(it->second) << std::endl;
//      f.close();
//    }
//  }

  const std::string &Parser::getFileName(void) const
  {
      return _file;
  }

  /*
  ** ROW
  */

  Row::Row(Parser* parser)
      : _parser(parser)
    ,_values(parser->columnCount())
    {}

  Row::~Row(void) {}

  void Row::clear()
  {
      _values.clear();
      _parser = nullptr;
  }

  unsigned int Row::size(void) const
  {
      return _values.size();
  }

  void Row::push(unsigned int pos, unsigned int index)
  {
      if(pos<=_values.size()){
          _values[pos] = index;
      }
  }
    
//  bool Row::set(const std::string &key, const std::string &value)
//  {
//      int pos = _parser->getHeaderPos(key);
//      if(pos >= 0)
//          _values[pos] = value;
//          return true;
//      return false;
//  }

  const std::string& Row::operator[](unsigned int pos) const
  {
       if (pos < _values.size())
           return _parser->getColumnElement(pos, _values[pos]);
       return _emptyStr;
  }

  const std::string& Row::operator[](const std::string &key) const
  {
      int pos = _parser->getHeaderPos(key);
      if (pos < _values.size())
          return _parser->getColumnElement(pos, _values[pos]);
      return _emptyStr;
  }

  const std::vector<std::string>& Row::getHeader() const
  {
      return _parser->getHeader();
  }

  void Row::dump(const char* info) const
  {
      const std::vector<std::string>& keys = getHeader();
      std::cout << std::string("Row dump start : ") << info << std::endl;

      std::string key;
      std::string value;
      for(int i = 0; i < keys.size(); i++){
          key = keys[i];
          value = (*this)[i];
          std::cout << key << "      " << value << std::endl;
      }
  }

//  std::ostream &operator<<(std::ostream &os, const Row &row)
//  {
//      for (unsigned int i = 0; i != row._values.size(); i++)
//          os << row._values[i] << " | ";
//
//      return os;
//  }
//
//  std::ofstream &operator<<(std::ofstream &os, const Row &row)
//  {
//    for (unsigned int i = 0; i != row._values.size(); i++)
//    {
//        os << row._values[i];
//        if (i < row._values.size() - 1)
//          os << ",";
//    }
//    return os;
//  }
}
